﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using DependencyAnalysis.DataModel;
using Microsoft.CSharp;
using Microsoft.CSharp.RuntimeBinder;

namespace theeg.DependencyAnalysis.CommonAddin.Models
{
    public interface IAssert
    {
        void AreEqual(object expected, object actual);
        void AreNotEqual(object expected, object actual);
        void IsTrue(bool value);
        void IsFalse(bool value);
        void IsTrue(Func<bool> predicate);
        void IsFalse(Func<bool> predicate);
        void Throws<TException>(Action action) where TException : Exception;
        void DoesNotThrow<TException>(Action action) where TException : Exception;
    }

    public static class RuleCompiler
    {
        private const string Header =
@"
using System;
using System.Linq;
using System.Text;
using DependencyAnalysis;
using DependencyAnalysis.DataModel;
using theeg.DependencyAnalysis.CommonAddin.Models;

namespace theeg.DependencyAnalysis.GeneratedRules
{
    public class <<name>> : ICompiledRule
    {
        public IDependencyModel Model { get; set; }

        public IVerifier Verifier { get; set; }

        public IAssert Assert { get; set; }

        public Metrics Metrics { get; set; }

        public void Execute()
        {
";

        private const string Footer =
@"
        }
    }
}
";

        public static ICompiledRule Compile(IRuleDescriptor rule)
        {
            var ruleName = "Rule" + Guid.NewGuid().ToString("N");
            string source = Header.Replace("<<name>>", ruleName) +
                            rule.Source + Footer;

            var options = new Dictionary<string, string>
                {
                    { "CompilerVersion", "v4.0" }
                };
            var provider = new CSharpCodeProvider(options);

            var parameters = new CompilerParameters
            {
                GenerateInMemory = true,
                GenerateExecutable = false
            };
            parameters.ReferencedAssemblies.Add(typeof(ICompiledRule).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(IDependencyModel).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(RuntimeBinderException).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(IQueryable).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(Expression).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(Func<bool>).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(ISet<bool>).Assembly.Location);

            CompilerResults results = provider.CompileAssemblyFromSource(parameters, source);

            if (results.Errors.Count != 0)
            {
                var stringBuilder = new StringBuilder();
                foreach (var error in results.Errors)
                {
                    stringBuilder.AppendLine(error.ToString());
                }
                throw new ArgumentException(stringBuilder.ToString());
            }
            var instance = results.CompiledAssembly.CreateInstance("theeg.DependencyAnalysis.GeneratedRules." + ruleName);
            return instance as
                ICompiledRule;
        }
    }
}

